package com.lizba.redis.redispool;

import redis.clients.jedis.Jedis;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 *      Redis连接池
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/9/1 22:12
 */
public class MasterDataRedisPool  {

    /** 连接地址 */
    private static String address;
    /** 连接端口 */
    private static int port;
    /** 最大连接数 */
    private static int maxTotal;
    /** 最长等待时间 */
    private static long maxWaitMillis;
    /** 空闲连接 */
    private static LinkedBlockingQueue<Jedis> idleConnections;
    /** 活动连接 */
    private static LinkedBlockingQueue<Jedis> activeConnections;
    /** 总连接数 */
    private static AtomicInteger count = new AtomicInteger(0);
    /** 锁 */
    private static ReentrantLock lock = new ReentrantLock();
    /** 时间记录 */
    private static ThreadLocal<Long> time = new ThreadLocal<>();

    /**
     * 初始化
     */
    static {
        address = System.getProperty("");
        port = Integer.parseInt(System.getProperty(""));
        maxTotal = Integer.parseInt(System.getProperty(""));
        maxWaitMillis = Long.parseLong(System.getProperty(""));
        idleConnections = new LinkedBlockingQueue<>(maxTotal);
        activeConnections = new LinkedBlockingQueue<>(maxTotal);
    }


    /**
     * 1、记录开始时间
     * 2、从空闲集合idleConnections中获取连接，获取连接成功则放入活动连接集合中activeConnections，返回连接
     * 3、空闲连接中无法获取成功，判断连接池是否已满，如果未满创建一个新的连接，放入activeConnections中，返回连接
     * 4、如果连接池已满，获取连接失败，则等待其他线程释放连接后获取，等待时间为maxWaitMillis，未超时获取成功，返回连接
     * 5、超时未获取连接，抛出异常
     *
     *
     * @return
     * @throws Exception
     */
    public Jedis create() throws Exception {

        lock.lock();
        time.set(System.currentTimeMillis());
        try {
            Jedis jedis = null;
            while (jedis == null) {
                // 有空闲，则直接返回
                jedis = idleConnections.poll();
                if (jedis != null) {
                    activeConnections.offer(jedis);
                    return jedis;
                }
                // 数量未达最大数据，创建新的连接
                if (count.get() < maxTotal) {
                    if (count.incrementAndGet() <= maxTotal) {
                        jedis = new Jedis(address, port);
                        activeConnections.offer(jedis);
                        return jedis;
                    } else {
                        count.decrementAndGet();
                    }
                }
                // 等待其他线程释放连接
                try {
                    jedis = idleConnections.poll(maxWaitMillis - (System.currentTimeMillis() - time.get()), TimeUnit.MILLISECONDS);
                    if (jedis != null) {
                        activeConnections.offer(jedis);
                        return jedis;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                // 判断是否超时等待，可能存在被中断需要continue
                if (maxWaitMillis <= (System.currentTimeMillis() - time.get())) {
                    throw new Exception("获取Redis连接超时！");
                } else {
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            time.remove();
            lock.unlock();
        }


        return null;
    }

    /**
     * 释放连接
     *
     * @param jedis
     */
    public void release(Jedis jedis) {
        if (jedis != null && activeConnections.remove(jedis)) {
            idleConnections.offer(jedis);
        }
    }
}
